#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <Judy.h>

#include "../model/globals.h"
#include "../normalization/normalized_datatypes.h"
#include "../normalization/globals.h"
#include "../normalization/print_utils.h"
#include "../normalization/utils.h"
#include "../preprocessing/utils.h"
#include "../util/queue.h"
#include "utils.h"


struct queue queues_to_process;
// to avoid multiple additions of the same concept name to the queues to process
char* in_queue;

void init_completion() {
	int i;
	NormalizedConcept* c;

	init_queue(&queues_to_process);
	in_queue = (char*) malloc(normalized_atomic_concept_count * sizeof(char));
	assert(in_queue != NULL);

	for (i = 0; i < normalized_atomic_concept_count ; ++i) {
		if (normalized_atomic_concepts[i]->containing_premises_count != 0) {
			c = normalized_atomic_concepts[i];
			// initially the queue and subsumer list of c contains only itself
			enqueue(&c->description.atomic->concepts_to_process, c);
			add_subsumer(c->description.atomic, c);
			// now push c to the queue of queues to be processed
			enqueue(&queues_to_process, c->description.atomic);
			in_queue[i] = 1;
		}
		else
			in_queue[i] = 0;
	}
}

void process_concept_name(NormalizedAtomicConcept* ac, NormalizedConcept* c) {
	int i, judy_return;
	Word_t index;
	NormalizedConcept* ex;

	// avoid adding an already existential restriction
	if (ac == c->description.atomic)
		return;

	for (i = 0; i < ac->number_of_filler_of; ++i) {
		index = 0;
		ex = get_create_normalized_exists(ac->filler_of[i]->role, c);
		J1F(judy_return, ac->filler_of[i]->subsumees, index);
		while (judy_return == 1) {
			if (!is_subsumer_of((NormalizedAtomicConcept*) index, ex)) {
				enqueue(&(((NormalizedAtomicConcept*) index)->concepts_to_process), ex);
				add_subsumer((NormalizedAtomicConcept*) index, ex);

				// now push (NormalizedAtomicConcept*) index to the queue to process it again
				if (!in_queue[((NormalizedAtomicConcept*) index)->id]) {
					enqueue(&queues_to_process, (NormalizedAtomicConcept*) index);
					in_queue[((NormalizedAtomicConcept*) index)->id] = 1;
				}

			}
			J1N(judy_return, ac->filler_of[i]->subsumees, index);
		}
	}
}

void process_existential(NormalizedAtomicConcept* ac, NormalizedConcept* c) {
	Word_t index = 0;
	int judy_return;
	NormalizedConcept* ex;

	J1F(judy_return, c->description.exists->filler->description.atomic->subsumers, index); 
	while (judy_return == 1) {
		if (c->description.exists->filler != (NormalizedConcept*) index) {
			ex = get_create_normalized_exists(c->description.exists->role, (NormalizedConcept*) index);
			if (!is_subsumer_of(ac, ex)) {
				// push_to_concepts_to_process(ac, ex);
				enqueue(&ac->concepts_to_process, ex);
				add_subsumer(ac, ex);

				// now push ac to the queue to process it again
				if (!in_queue[ac->id]) {
					enqueue(&queues_to_process, ac);
					in_queue[ac->id] = 1;
				}

			}
		}
		J1N(judy_return, c->description.exists->filler->description.atomic->subsumers, index); 
	}
}

void process_rhs(NormalizedAtomicConcept* ac, NormalizedSubClassAxiom* ax) {
	int judy_return;
	Word_t index;

	switch (ax->rhs->type) {
		case NORMALIZED_ATOMIC_CONCEPT:
			if (!is_subsumer_of(ac, ax->rhs) && ax->rhs->containing_premises_count != 0)
				// push_to_concepts_to_process(ac, ax->rhs);
				enqueue(&ac->concepts_to_process, ax->rhs);
			add_subsumer(ac,ax->rhs);
			process_concept_name(ac, ax->rhs);
			break;
		case NORMALIZED_EXISTENTIAL_RESTRICTION:
			if (!is_subsumer_of(ac, ax->rhs) && ax->rhs->containing_premises_count != 0) 
				enqueue(&ac->concepts_to_process, ax->rhs);
			add_subsumer(ac,ax->rhs);
			process_existential(ac, ax->rhs);
			break;
		case NORMALIZED_CONJUNCTION:
			index = 0;
			J1F(judy_return, ax->rhs->description.conj->conjuncts, index);
			if (judy_return == 0) {
				fprintf(stderr,"no conjunct in conjunction, aborting\n");
				exit(-1);
			}
			while (judy_return == 1) {
				switch (((NormalizedConcept*) index)->type) {
					case NORMALIZED_ATOMIC_CONCEPT:
						if (!is_subsumer_of(ac, (NormalizedConcept*) index) && ((NormalizedConcept*) index)->containing_premises_count != 0) 
							enqueue(&ac->concepts_to_process, (NormalizedConcept*) index);
						add_subsumer(ac, (NormalizedConcept*) index);
						process_concept_name(ac, (NormalizedConcept*) index);
						break;
					case NORMALIZED_EXISTENTIAL_RESTRICTION:
						if (!is_subsumer_of(ac, (NormalizedConcept*) index) && ((NormalizedConcept*) index)->containing_premises_count != 0) 
							enqueue(&ac->concepts_to_process, (NormalizedConcept*) index);
						add_subsumer(ac, (NormalizedConcept*) index);
						
						process_existential(ac, (NormalizedConcept*) index);
						break;
				}
				J1N(judy_return, ax->rhs->description.conj->conjuncts, index);
			}
			break;
		default:
			fprintf(stderr, "unknown normalized concept type, aborting!\n");
			exit(-1);
	}
}


void complete_kb() {
	NormalizedAtomicConcept* ac;
	NormalizedConcept* c;
	int i;

	init_completion();

	while (!queue_empty(&queues_to_process)) {
		ac = dequeue(&queues_to_process);
		in_queue[ac->id] = 0;

		// printf("======== concept name: %s ========\n",ac->name);
		while (!queue_empty(&ac->concepts_to_process)) {
			c = dequeue(&ac->concepts_to_process);

			// printf("processing concept: ");
			// print_normalized_concept(c);
			// printf("\n");
			// printf("containing premises counts: %d %d\n",c->singleton_premises_count, c->containing_premises_count);
			// for (i = 0; i < c->singleton_premises_count; ++i) {
				// printf("singleton ax: ");
				// print_normalized_axiom(c->singleton_premises[i]);
				// printf("fire!\n");
				// push_rhs_to_concepts_to_process(ac, c->singleton_premises[i]->body.subclass_ax);
				// add_rhs_to_subsumers(ac, c->singleton_premises[i]->body.subclass_ax);
				// process_rhs(ac, c->singleton_premises[i]->body.subclass_ax);
			// }
			for (i = 0; i < c->containing_premises_count; ++i) {
				// printf("containing ax: ");
				// print_normalized_axiom(c->containing_premises[i]);
				if (subsumes(ac, c->containing_premises[i]->body.subclass_ax->lhs)) 
					// add the rhs of ax to the stack of ac
					// push_rhs_to_concepts_to_process(ac, c->containing_premises[i]->body.subclass_ax);
					// add_rhs_to_subsumers(ac, c->containing_premises[i]->body.subclass_ax);
					process_rhs(ac, c->containing_premises[i]->body.subclass_ax);
			}
		}
	}
}

void process(NormalizedAtomicConcept* ac, NormalizedConcept* c) {
	switch (c->type) {
		case NORMALIZED_ATOMIC_CONCEPT:
			process_concept_name(ac, c);
			break;
		case NORMALIZED_EXISTENTIAL_RESTRICTION:
			process_existential(ac, c);
			break;
		default:
			break;
	}
}
	

void add_subsumer(NormalizedAtomicConcept* ac, NormalizedConcept* c) {
	int judy_return;

	switch (c->type) {
		case NORMALIZED_ATOMIC_CONCEPT:
/*
			index = 0;
			J1F(judy_return, c->description.atomic->subsumers, index);
			while (judy_return == 1) {
				if (!is_subsumer_of(ac, (NormalizedConcept*) index)) {
					push_to_concepts_to_process(ac, (NormalizedConcept*) index);
					J1S(judy_return1, ac->subsumers, index);
				}
				process(ac, (NormalizedConcept*) index);
				J1N(judy_return, c->description.atomic->subsumers, index);
			}
*/
			J1S(judy_return, ac->subsumers, (Word_t) c);
			if (judy_return == JERR) {
				fprintf(stderr, "could not add subsumer\n");
				exit(-1);
			}
/*
			J1S(judy_return, c->description.atomic->subsumees, (Word_t) ac);

			index = 0;
			J1F(judy_return, ac->subsumees, index);
			while (judy_return == 1) {
				J1S(judy_return1, ((NormalizedAtomicConcept*) index)->subsumers, (Word_t) c);
				J1N(judy_return, ac->subsumees, index);
			}
*/

			break;
		case NORMALIZED_EXISTENTIAL_RESTRICTION:
			J1S(judy_return, c->description.exists->subsumees, (Word_t) ac);
			if (judy_return == JERR) {
				fprintf(stderr, "could not add subsumee\n");
				exit(-1);
			}
			break;
		default:
			fprintf(stderr, "add_subsumer: no other normalized concept type possible here, aborting!\n");
			exit(-1);
	}
}
